package com.dbms;

import com.dbms.common.Column;
import com.dbms.common.QueryResult;
import com.dbms.common.Tuple;
import com.dbms.DbmsCore;
import java.io.File;

/**
 * 一个专门用于演示DBMS事务管理和崩溃恢复功能的独立测试程序。
 * 本程序分为两部分：
 * 1. 演示手动的 BEGIN, COMMIT, ROLLBACK 命令。
 * 2. 演示在事务执行中途模拟数据库崩溃，重启后系统如何自动恢复数据一致性。
 *
 * 【已修正】确保测试使用独立的文件，避免干扰其他数据库实例。
 */
public class TransactionTest {

    private static final String DB_FILE_NAME = "transaction_test.db";
    private static final String LOG_FILE_NAME = "transaction_test.log";

    public static void main(String[] args) throws InterruptedException {
        System.out.println("============== 事务管理功能演示开始 ==============\n");

        // 运行第一部分：手动提交与回滚演示
        demonstrateCommitAndRollback();
/**
 * 第一部分：演示 BEGIN, COMMIT, 和 ROLLBACK.
 * 模拟一个经典的银行转账场景。
 */
        System.out.println("\n\n============== 手动事务演示结束 ==============");
        System.out.println("============== 即将开始崩溃恢复演示 ==============\n\n");

        // 稍作停顿，便于观察
        Thread.sleep(2000);

        // 运行第二部分：崩溃恢复演示
        demonstrateCrashRecovery();

        System.out.println("\n============== 事务管理功能演示结束 ==============");
    }

    /**
     * 第一部分：演示 BEGIN, COMMIT, 和 ROLLBACK.
     */
    private static void demonstrateCommitAndRollback() {
        System.out.println("--- Part 1: 手动事务控制 (COMMIT & ROLLBACK) ---");
        System.out.println("    场景：银行转账，将 100 元从账户 A 转到账户 B。");
        System.out.println("--------------------------------------------------\n");

        // 准备一个干净的环境
        new File(DB_FILE_NAME).delete();
        new File(LOG_FILE_NAME).delete();
        // 【核心修正】使用带有文件名的构造函数
        DbmsCore dbms = new DbmsCore(DB_FILE_NAME, LOG_FILE_NAME);

        // 1. 初始化数据
        System.out.println("--- 1. 初始化账户数据 ---");
        dbms.processSql("CREATE TABLE bank_accounts (id INT PRIMARY KEY, name VARCHAR(50), balance INT);");
        dbms.processSql("INSERT INTO bank_accounts VALUES (1, 'Account_A', 1000);");
        dbms.processSql("INSERT INTO bank_accounts VALUES (2, 'Account_B', 500);");
        System.out.println("初始状态:");
        printResult(dbms.processSql("SELECT * FROM bank_accounts;"));

        // 2. 演示 ROLLBACK
        System.out.println("\n--- 2. 演示 ROLLBACK (回滚) ---");
        System.out.println("--> BEGIN; (开启事务)");
        dbms.processSql("BEGIN;");

        System.out.println("--> UPDATE accounts SET balance = balance - 100 WHERE id = 1; (A账户扣款)");
        dbms.processSql("UPDATE bank_accounts SET balance = balance - 100 WHERE id = 1;");

        System.out.println("--> UPDATE accounts SET balance = balance + 100 WHERE id = 2; (B账户收款)");
        dbms.processSql("UPDATE bank_accounts SET balance = balance + 100 WHERE id = 2;");

        System.out.println("\n事务内部，提交前的数据状态 (已改变):");
        printResult(dbms.processSql("SELECT * FROM bank_accounts;"));

        System.out.println("--> ROLLBACK; (操作失误，决定回滚事务)");
        dbms.processSql("ROLLBACK;");

        System.out.println("\n事务回滚后的数据状态 (已恢复到初始状态):");
        printResult(dbms.processSql("SELECT * FROM bank_accounts;"));
        System.out.println("✅ 结论：ROLLBACK 成功撤销了事务中的所有更改。\n");


        // 3. 演示 COMMIT
        System.out.println("\n--- 3. 演示 COMMIT (提交) ---");
        System.out.println("--> BEGIN; (重新开启事务)");
        dbms.processSql("BEGIN;");

        System.out.println("--> UPDATE accounts SET balance = balance - 100 WHERE id = 1; (A账户扣款)");
        dbms.processSql("UPDATE bank_accounts SET balance = balance - 100 WHERE id = 1;");

        System.out.println("--> UPDATE accounts SET balance = balance + 100 WHERE id = 2; (B账户收款)");
        dbms.processSql("UPDATE bank_accounts SET balance = balance + 100 WHERE id = 2;");

        System.out.println("--> COMMIT; (操作无误，决定提交事务)");
        dbms.processSql("COMMIT;");

        System.out.println("\n事务提交后的数据状态 (更改已永久生效):");
        printResult(dbms.processSql("SELECT * FROM bank_accounts;"));
        System.out.println("✅ 结论：COMMIT 成功将事务中的更改持久化。");

        dbms.shutdown();
    }


    /**
     * 第二部分：演示数据库崩溃后的自动恢复。
     */
    private static void demonstrateCrashRecovery() {
        System.out.println("--- Part 2: 数据库崩溃后的自动恢复 ---");
        System.out.println("    场景：一个事务在执行过程中，未提交时，数据库突然崩溃。");
        System.out.println("----------------------------------------------------\n");

        // 准备一个干净的环境
        new File(DB_FILE_NAME).delete();
        new File(LOG_FILE_NAME).delete();
        // 【核心修正】使用带有文件名的构造函数
        DbmsCore dbms = new DbmsCore(DB_FILE_NAME, LOG_FILE_NAME);

        // 1. 初始化数据
        System.out.println("--- 1. 初始化数据 ---");
        dbms.processSql("CREATE TABLE products (id INT PRIMARY KEY, name VARCHAR(50), stock INT);");
        dbms.processSql("INSERT INTO products VALUES (101, 'Apple', 50);");
        dbms.processSql("INSERT INTO products VALUES (102, 'Banana', 80);");
        System.out.println("数据库崩溃前的初始状态:");
        printResult(dbms.processSql("SELECT * FROM products;"));

        // 2. 执行一个未提交的事务
        System.out.println("\n--- 2. 执行一个未完成的事务 ---");
        System.out.println("--> BEGIN; (开启事务)");
        dbms.processSql("BEGIN;");

        System.out.println("--> INSERT INTO products VALUES (103, 'Cherry', 99); (插入新品)");
        dbms.processSql("INSERT INTO products VALUES (103, 'Cherry', 99);");

        System.out.println("\n事务内部，崩溃前的脏数据（注意，这些更改尚未提交）:");
        printResult(dbms.processSql("SELECT * FROM products;"));

        // 3. 模拟数据库崩溃与重启
        System.out.println("\n--- 3. 模拟数据库崩溃并重启 ---");
        System.out.println("    (不调用 shutdown()，直接创建一个新的 DbmsCore 实例加载旧文件)");
        // !! 这里我们没有调用 dbms.shutdown()，直接让旧的 dbms 对象被废弃
        // 【核心修正】使用带有文件名的构造函数
        dbms = new DbmsCore(DB_FILE_NAME, LOG_FILE_NAME); // 这会触发构造函数中的 recover() 逻辑
        System.out.println("数据库重启完成，恢复过程已自动执行。\n");

        // 4. 验证数据状态
        System.out.println("--- 4. 验证恢复后的数据状态 ---");
        System.out.println("恢复后的最终数据:");
        printResult(dbms.processSql("SELECT * FROM products;"));
        System.out.println("✅ 结论：数据库成功地将未提交事务的所有更改（UPDATE和INSERT）自动回滚，");
        System.out.println("   数据恢复到了事务开始前的一致状态！");

        dbms.shutdown();
    }

    /**
     * 一个简单的辅助方法，用于将查询结果打印到控制台。
     * @param result 要打印的查询结果对象。
     */
    private static void printResult(QueryResult result) {
        if (result == null) return;
        if (result.isSuccess) {
            if (result.schema != null && !result.tuples.isEmpty()) {
                // 打印表头
                for (Column col : result.schema.getColumns()) {
                    System.out.print(col.getColumnName() + "\t | ");
                }
                System.out.println("\n-------------------------------------");

                // 打印每一行数据
                for (Tuple tuple : result.tuples) {
                    System.out.println(tuple.toString());
                }
            }
            // 打印成功消息 (如果适用)
            if (result.message != null && result.tuples.isEmpty()) {
                System.out.println(result.message);
            }
        } else {
            // 打印错误消息
            System.err.println("ERROR: " + result.message);
        }
    }
}